package com.ruge.sorm.core;

import com.ruge.sorm.bean.ColumnInfo;
import com.ruge.sorm.bean.TableInfo;
import com.ruge.sorm.util.JDBCUtils;
import com.ruge.sorm.util.ReflectUtils;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

/**
 * 负责针对MySQL数据库的查询（对外提供服务的核心类）
 *
 * @author admin
 * @version 2023/10/21 15:07
 **/
public class MysqlQuery implements Query {
    /**
     * 直接执行一个DML语句
     *
     * @param sql    sql语句
     * @param params 参数
     * @return 执行sql语句之后，影响记录的行数
     */
    @Override
    public int executeDML(String sql, Object[] params) {
        Connection conn = DBManager.getConnection();
        int count = 0;
        PreparedStatement ps = null;

        try {
            ps = conn.prepareStatement(sql);

            // 给sql设置参数
            JDBCUtils.handleParams(params, ps);
            System.out.println(ps);
            count = ps.executeUpdate();
        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            DBManager.close(ps, conn);
        }
        return count;
    }

    /**
     * 将一个对象存储到数据库中
     * 把对象中不为null的属性往数据库中存储,如果数字为null则放0。
     *
     * @param obj 要存储的对象
     */
    @Override
    public void insert(Object obj) {
// obj --> 表中 | insert into 表名 (id,name,pwd) values (?,?,?)
        Class c = obj.getClass();

        // 存储sql的参数对象
        List<Object> params = new ArrayList<>();

        TableInfo tableInfo = TableContext.poClassTableMap.get(c);
        StringBuilder sql = new StringBuilder("insert into " + tableInfo.getTname() + " (");

        // 计算不为空的属性值
        int countNotNullField = 0;

        // 目前，只能处理数据库来维护自增主键的方式
        Field[] fs = c.getDeclaredFields();
        for (Field f : fs) {
            String fieldName = f.getName();
            Object fieldValue = ReflectUtils.invokeGet(fieldName, obj);
            if (null != fieldValue) {
                countNotNullField++;
                sql.append(fieldName + ",");
                params.add(fieldValue);
            }
        }
        sql.setCharAt(sql.length() - 1, ')');

        sql.append(" values (");
        for (int i = 0; i < countNotNullField; i++) {
            sql.append("?,");
        }
        sql.setCharAt(sql.length() - 1, ')');

        executeDML(sql.toString(), params.toArray());
    }

    /**
     * 删除clazz表示类对应的表中的记录（指定主键的值id的记录）
     *
     * @param clazz 跟表对应的类Class对象
     * @param id    主键的值
     */
    @Override
    public void delete(Class clazz, Object id) {
        // User.class,1 --> delete from user where id = 1

        // 通过Class对象找TableInfo User --> User,user
        TableInfo tableInfo = TableContext.poClassTableMap.get(clazz);
        // 获得主键
        ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();

        String sql = "delete from " + tableInfo.getTname() + " where " + onlyPriKey.getName() + "= ? ";

        executeDML(sql, new Object[]{id});
    }

    /**
     * 删除对象在数据库中对应的记录（对象所在的类对应到表，对象主键的值对应到记录）
     *
     * @param obj
     */
    @Override
    public void delete(Object obj) {
        Class<?> c = obj.getClass();
        TableInfo tableInfo = TableContext.poClassTableMap.get(c);
        ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey(); // 主键

        // 通过反射机制，调用属性对应的的get方法或set方法
        Object priKeyValue = ReflectUtils.invokeGet(onlyPriKey.getName(), obj);

        delete(c, priKeyValue);
    }

    /**
     * 更新对象对应的记录，并且只更新指定的字段的值
     *
     * @param obj        所要更新的对象
     * @param fieldNames 更新的属性列表
     * @return 执行sql语句之后，影响记录的行数
     */
    @Override
    public int update(Object obj, String[] fieldNames) {
        // obj{"uname","pwd"} --> update 表名 set uname = ? ,pwd = ?,where id = ?
        Class<?> c = obj.getClass();
        List<Object> params = new ArrayList<>();
        TableInfo tableInfo = TableContext.poClassTableMap.get(c);
        ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey(); // 获得唯一的主键
        StringBuilder sql = new StringBuilder("update " + tableInfo.getTname() + " set ");

        for (String fieldName : fieldNames) {
            Object fValue = ReflectUtils.invokeGet(fieldName, obj);
            params.add(fValue);
            sql.append(fieldName + " = ? ,");
        }
        sql.setCharAt(sql.length() - 1, ' ');
        sql.append("where ");
        sql.append(onlyPriKey.getName() + " = ? ");

        // 获取主键的值
        params.add(ReflectUtils.invokeGet(onlyPriKey.getName(), obj));
        executeDML(sql.toString(), params.toArray());
        return 0;
    }

    /**
     * 查询返回多行记录，并将每行记录封装到clazz指定的类的对象中
     *
     * @param sql    查询语句
     * @param clazz  封装数据的javabean类的Class对象
     * @param params sql的参数
     * @return 查询到的结果
     */
    @Override
    public List queryRows(String sql, Class clazz, Object[] params) {
        Connection conn = DBManager.getConnection();
        List list = null;// 存放查询结果的容器
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = conn.prepareStatement(sql);

            // 给sql设置参数
            JDBCUtils.handleParams(params, ps);
            System.out.println(ps);
            rs = ps.executeQuery();

            ResultSetMetaData metaData = rs.getMetaData();
            // 多行
            while (rs.next()) {
                if (null == list) {
                    list = new ArrayList();
                }
                Object rowObject = clazz.newInstance(); //调用Javabean的无参数构造器

                // 多列 select username,pwd,age from user where id = ?
                for (int i = 0; i < metaData.getColumnCount(); i++) {
                    String columnName = metaData.getColumnLabel(i + 1); // userName
                    Object columnValue = rs.getObject(i + 1);

                    // 调用rowObject的setUsername方法，将前面说的columnValue的值设置进去
                    ReflectUtils.invokeSet(columnName, rowObject, columnValue);
                }

                list.add(rowObject);
            }

        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            DBManager.close(ps, conn);
        }

        return list;
    }

    /**
     * 查询返回一行记录，并将该记录封装到clazz指定的类的对象中
     *
     * @param sql    查询语句
     * @param clazz  封装数据的javabean类的Class对象
     * @param params sql的参数
     * @return 查询到的结果
     */
    @Override
    public Object queryUniqueRow(String sql, Class clazz, Object[] params) {
        List list = queryRows(sql, clazz, params);
        return list == null && list.size() > 0 ? null : list.get(0);
    }

    /**
     * 查询返回一个值（一行一列），并将该值返回
     *
     * @param sql    查询语句
     * @param params sql的参数
     * @return 查询到的结果
     */
    @Override
    public Object queryValue(String sql, Object[] params) {
        Connection conn = DBManager.getConnection();
        Object value = null;// 存放查询结果的对象
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = conn.prepareStatement(sql);

            // 给sql设置参数
            JDBCUtils.handleParams(params, ps);
            System.out.println(ps);
            rs = ps.executeQuery();

            // 多行
            while (rs.next()) {
                // select count(*) from user
                value = rs.getObject(1);
            }

        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            DBManager.close(ps, conn);
        }

        return value;
    }

    /**
     * 查询返回一个数字（一行一列），并将该值返回
     *
     * @param sql    查询语句
     * @param params sql的参数
     * @return 查询到的数字
     */
    @Override
    public Number queryNumber(String sql, Object[] params) {
        return (Number) queryValue(sql, params);
    }
}
